package com.phone.servlet;

import com.phone.DBHelper;
import com.phone.ParameterCheck;
import com.phone.entity.Results;
import com.phone.exception.ErrorCode;
import com.phone.exception.ErrorException;
import com.phone.handle.EntityHandle;
import com.phone.entity.Result;
import org.hibernate.Session;
import org.json.JSONObject;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.List;

/**
 * Created by tisen on 2017/9/7.
 */
public abstract class EntityServlet<T> extends HttpServlet {

    public static final String ERROR = "error";
    public static final String SUCCESS = "success";

    private static final String ACTION = "action";
    private static final String ACTION_SAVE = "save";
    private static final String ACTION_DELETE = "delete";
    private static final String ACTION_UPDATE = "update";
    private static final String ACTION_GET = "get";
    private static final String ACTION_SEARCH = "search";
    private static final String ACTION_SQL = "sql";

    private Class<T> entityClass;
    private String entityName;
    protected Session session;

    @Override
    public void init() throws ServletException {
        super.init();
        Type genType = getClass().getGenericSuperclass();
        Type[] params = ((ParameterizedType) genType).getActualTypeArguments();
        entityClass = (Class) params[0];
        entityName = entityClass.getSimpleName().toLowerCase();
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        session = DBHelper.getSession();
        ParameterCheck parameterCheck = new ParameterCheck(request);
        JSONObject jsonObject = Control(parameterCheck, session);
        if (session != null && session.isOpen())
            session.close();
        out(response, jsonObject);
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req, resp);
    }

    private void out(HttpServletResponse response, JSONObject control) throws IOException {
        response.setContentType("text/html;charset=UTF-8");
        PrintWriter out = response.getWriter();
        out.println(control.toString());
        out.flush();
        out.close();
    }

    public JSONObject Control(ParameterCheck check, Session session) {
        session.beginTransaction();
        EntityHandle handle = getHandle(session);
        JSONObject jsonObject = new JSONObject();
        try {
            String action = check.getParameterString(ACTION, false);
            switch (action) {
                case ACTION_SAVE:
                    Result<String> result = new Result<>();
                    handle.checkOwnerId(getOwnerID(check));
                    result.setObject((String) handle.add(getEntity(check)));
                    result.setResult(SUCCESS);
                    result.setReason("add " + entityName + " success");
                    jsonObject = new JSONObject(result);
                    break;
                case ACTION_DELETE:
                    Result<String> result1 = new Result<>();
                    handle.delete(getID(check));
                    result1.setResult(SUCCESS);
                    result1.setReason("delete " + entityName + " success");
                    jsonObject = new JSONObject(result1);
                    break;
                case ACTION_UPDATE:
                    Result<String> result2 = new Result<>();
                    handle.update(getID(check), getUpdateEntity(check));
                    result2.setResult(SUCCESS);
                    result2.setReason("update " + entityName + " success");
                    jsonObject = new JSONObject(result2);
                    break;
                case ACTION_GET:
                    Result<T> result3 = new Result<>();
                    result3.setObject((T) handle.get(getID(check)));
                    result3.setResult(SUCCESS);
                    result3.setReason("get " + entityName + " success");
                    jsonObject = new JSONObject(result3);
                    break;
                case ACTION_SEARCH:
                    Results<T> results = new Results<>();
                    int pageSize = getPageSize(check);
                    int page = getPage(check);
                    List<T> list = handle.search(getSearch(check));

                    int size = list.size();
                    int fromIndex = pageSize * (page - 1) <= size ? pageSize * (page - 1) : size;
                    int toIndex = pageSize * page <= size ? pageSize * page : size;
                    if ((page - 1) * pageSize > size) {
                        results = new Results(ERROR, "page beyond", list.size(), pageSize, page, null);
                    } else {
                        results = new Results(SUCCESS, "search " + entityName + " success", list.size(), pageSize, page,
                                list.subList(fromIndex, toIndex));
                    }
                    jsonObject = new JSONObject(results);
                    break;
                case ACTION_SQL:

                    Results<T> results1 = new Results<>();
                    int pageSize1 = getPageSize(check);
                    int page1 = getPage(check);
                    List<T> list1 = handle.search(getSql(check));

                    int size1 = list1.size();
                    int fromIndex1 = pageSize1 * (page1 - 1) <= size1 ? pageSize1 * (page1 - 1) : size1;
                    int toIndex1 = pageSize1 * page1 <= size1 ? pageSize1 * page1 : size1;
                    if ((page1 - 1) * pageSize1 > size1) {
                        results1 = new Results(ERROR, "page beyond", list1.size(), pageSize1, page1, null);
                    } else {
                        results1 = new Results(SUCCESS, "search " + entityName + " success", list1.size(), pageSize1, page1,
                                list1.subList(fromIndex1, toIndex1));
                    }
                    jsonObject = new JSONObject(results1);
                    break;
                default:
                    jsonObject = otherHandle(action, check, session);
                    break;
            }
            session.getTransaction().commit();
        } catch (Exception e) {
            Result result = new Result();
            result.setResult(ERROR);
            result.setReason(e.getMessage());
            jsonObject = new JSONObject(result);
            session.getTransaction().rollback();
            e.printStackTrace();
        } finally {
            if (session != null && session.isOpen())
                session.close();
            return jsonObject;
        }
    }

    protected abstract JSONObject otherHandle(String action, ParameterCheck check, Session session) throws Exception;

    protected static final String ID = "id";
    protected static final String PAGE = "page";
    protected static final String PAGE_SIZE = "pagesize";

    public abstract T getEntity(ParameterCheck check) throws Exception;

    public abstract T getUpdateEntity(ParameterCheck check) throws Exception;

    public abstract EntityHandle getHandle(Session session);

    public String getSql(ParameterCheck check) throws Exception {
        return check.getParameterString("sql", false);
    }

    public abstract String getSearch(ParameterCheck check) throws Exception;

    public abstract String getOwnerID(ParameterCheck check) throws Exception;

    public String getID(ParameterCheck check) throws Exception {
        return check.getParameterString(ID, false);
    }

    public int getPage(ParameterCheck check) throws Exception {
        return check.getParameterInt(PAGE, true, 1);
    }

    public int getPageSize(ParameterCheck check) throws Exception {
        return check.getParameterInt(PAGE_SIZE, true, 10);
    }


    public JSONObject save(ParameterCheck check, Session session) {
        return null;
    }

    public JSONObject delete(ParameterCheck check, Session session) {
        return null;
    }

    public JSONObject update(ParameterCheck check, Session session) {
        return null;
    }

    public JSONObject get(ParameterCheck check, Session session) {
        return null;
    }

    public JSONObject search(ParameterCheck check, Session session) {
        return null;
    }


}
